package com.tools.web.interceptor.quota;

import com.tools.common.object.Note;
import com.tools.common.thread.Timeunit;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

import java.util.concurrent.TimeUnit;


/**
 * 使用 RedisTemplate 客户端操作 Redis 实现配额限制拦截器
 * 【注意】：业务成功后必须通过该拦截器实例的 incrSuccessfulCount 方法进行次数自增。
 * */
@Note("使用 RedisTemplate 客户端操作 Redis 实现配额限制拦截器." +
        "【注意】：业务成功后必须通过该拦截器实例的 incrSuccessfulCount 方法进行次数自增。")
public final class RedisTemplateQuotaInterceptor extends QuotaInterceptor {

    @Note("RedisTemplate 操作 Redis 的客户端实例")
    private final RedisTemplate<String, String> cache;

    public RedisTemplateQuotaInterceptor(RedisTemplate<String, String> cache, QuotaSource source) {
        super(source, cache != null, "RedisTemplate 客户端实例不能为 null");
        this.cache = cache;
    }


    @Override
    public void incrSuccessfulCount(String uniqueID, String action, long createExpiration, Timeunit createTimeunit) {
        if(createExpiration < 1) return;
        if(createTimeunit == null) throw new NullPointerException("过期时间单位为 null");
        String key = super.protected_getCachePrefix() + action + ':' + uniqueID;
        synchronized (key) {
            Integer successFulCount = this.protected_getSuccessfulCount(key);
            ValueOperations<String, String> opsForValue = this.cache.opsForValue();
            if(successFulCount == null) {
                opsForValue.set(key, Integer.toString(1),
                        Timeunit.toSecond(createExpiration, createTimeunit), TimeUnit.SECONDS);
                return;
            }
            long ttl = this.protected_getRemainder(key);
            if(ttl < 1) {
                opsForValue.set(key, Integer.toString(1),
                        Timeunit.toSecond(createExpiration, createTimeunit), TimeUnit.SECONDS);
                return;
            }
            opsForValue.set(key, Integer.toString(successFulCount + 1), ttl, TimeUnit.SECONDS);
        }
    }

    @Override
    protected Integer protected_getSuccessfulCount(String key) {
        String s = this.cache.opsForValue().get(key);
        return (s == null) ? null : Integer.parseInt(s);
    }

    @Override
    protected long protected_getRemainder(String key) {
        Long ttl = this.cache.getExpire(key);
        return (ttl == null) ? -1 : ttl;
    }
}
